#include <bits/stdc++.h>
using std::cerr;
using std::cin;
using std::cout;
using std::endl;
using pii = std::pair<int64_t, int64_t>;
void solve();
int main()
{
    freopen("detect.in", "r", stdin);
    freopen("detect.out", "w", stdout);
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int T = 1;
    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

struct frac
{
    __int128_t A, B;
    void simplify()
    {
        __int128_t g = std::__gcd(A, B);
        A /= g;
        B /= g;
    }
    frac()
    {
        A = 0, B = 1;
    }
    frac(__int128_t x)
    {
        A = x, B = 1;
    }
    frac(__int128_t a, __int128_t b)
    {
        A = a, B = b;
        simplify();
    }
    bool operator<(const frac &b)
    {
        __int128_t lhs = (__int128_t)A * b.B;
        __int128_t rhs = (__int128_t)B * b.A;
        return lhs < rhs;
    }
    bool operator==(const frac &b)
    {
        return (A == b.A) && (B == b.B);
    }
    int64_t integer()
    { // * 4
        int64_t x = (A / B) << 2;
        if (A % B)
        {
            return x + 2;
        }
        else
        {
            return x;
        }
    }
    void print()
    {
        cout << (int64_t)A;
        if (B != 1)
        {
            cout << "/" << (int64_t)B;
        }
    }
};
struct interval
{
    frac l = -1, r = -1;
    bool l_ok = false, r_ok = false;
    interval(frac ll, frac rr, bool lok, bool rok)
    {
        if (ll < rr or ll == rr)
        {
            l = ll;
            r = rr;
            l_ok = lok;
            r_ok = rok;
        }
    }
    bool operator==(const interval &b)
    {
        return (l == b.l) && (r == b.r) && (l_ok == b.l_ok) && (r_ok == b.r_ok);
    }
    // void print() {
    //     cout << (l_ok ? '[' : '(');
    //     l.print();
    //     cout << (',');
    //     cout << (' ');
    //     r.print();
    //     cout << (r_ok ? ']' : ')');
    // }
};
const interval EMPTY = interval(-1, -1, false, false);
int64_t n, m, L, V;
interval get_interval(int64_t d, int64_t v0, int64_t a)
{
    if (a == 0)
    {
        if (v0 <= V)
        {
            return EMPTY;
        }
        else
        {
            return interval(d, L, true, true);
        }
    }
    else if (a > 0)
    {
        if (v0 > V)
        {
            return interval(d, L, true, true);
        }
        else if (v0 == V)
        {
            return interval(d, L, false, true);
        }
        else
        {
            frac point = frac(2 * a * d + V * V - v0 * v0, 2 * a); // d + (V^2 - v0^2) / 2a
            if (frac(L) == point or frac(L) < point)
            {
                return EMPTY;
            }
            else
            {
                return interval(point, L, false, true);
            }
        }
    }
    else if (a < 0)
    {
        if (v0 <= V)
        {
            return EMPTY;
        }
        else
        {
            frac point = frac(2ll * a * d + V * V - v0 * v0, 2 * a); // d + (V^2 - v0^2) / 2a
            if (frac(L) < point)
            {
                return interval(d, L, true, true);
            }
            else
            {
                return interval(d, point, true, false);
            }
        }
    }
}

std::vector<int64_t> detectors;
std::vector<std::pair<int64_t, int64_t>> constraints;
bool update(interval LR)
{
    if (LR == EMPTY)
    {
        return 0;
    }
    // LR.l.print();
    // cout << ' ' << LR.l_ok << endl;
    int64_t l = LR.l.integer();
    if (LR.l_ok == false)
    {
        ++l;
    }
    int64_t r = LR.r.integer();
    if (LR.r_ok == false)
    {
        --r;
    }
    auto it = std::lower_bound(detectors.begin(), detectors.end(), l);
    // std::cerr << l << std::endl;
    // assert(it == detectors.begin());
    int64_t L = it - detectors.begin();
    it = std::upper_bound(detectors.begin(), detectors.end(), r);
    int64_t R = it - detectors.begin() - 1;
    if (R < 0 || L < 0 || L > R || R >= m || L >= m)
    {
        return false;
    }
    else
    {
        constraints.emplace_back(L, R);
        return true;
    }
}
void solve()
{
    detectors.clear();
    constraints.clear();
    cin >> n >> m >> L >> V;
    std::vector<interval> overspeed;
    for (int64_t i = 1; i <= n; ++i)
    {
        int64_t d, v0, a;
        cin >> d >> v0 >> a;
        overspeed.push_back(get_interval(d, v0, a));
    }
    for (int64_t i = 0; i < m; ++i)
    {
        int64_t p;
        cin >> p;
        detectors.push_back(p << 2); // * 4
    }
    detectors.push_back(INT_MAX);
    int64_t cntos = 0;
    for (interval i : overspeed)
    {
        cntos += update(i);
    }
    cout << cntos << " ";
    std::vector<int64_t> mini(m, INT_MAX);
    for (pii i : constraints)
    {
        // cerr << i.first << ' ' << i.second << endl;
        mini[i.first] = std::min(mini[i.first], i.second);
        // assert(i.first < 0);
    }
    std::vector<pii> c;
    //this is monotonic stack
    for (int64_t i = 0; i < m; ++i)
    {
        if (mini[i] != INT_MAX)
        {
            while (c.size() && mini[i] <= c.back().second)
            {
                c.pop_back();
            }
            c.emplace_back(i, mini[i]);
        }
    }
    c.emplace_back(INT_MAX, INT_MAX);
    int64_t res = 0, cnt = 1;
    for (int64_t i = 1; i < c.size(); ++i)
    {
        if (c[i].first > c[i - 1].second)
        {
            res += ((cnt == 1) ? (1) : (cnt - 1));
            cnt = 1;
        }
        else
        {
            cnt++;
        }
    }
    cout << m - res << endl;
}
/* ??pts
    Stats:
    SAMPLE        TIME    MEMORY
    detect1.in   0.00s    3.428M
    detect2.in   0.00s    3.544M
    detect3.in   0.44s   13.924M
    detect4.in   0.54s   13.684M
    detect5.in   ======WA====== ??????
*/